<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>数独解题器 chalide.cn</title>
<meta name="author" content="yujianyue, 15058593138@qq.com">

    <style>
        :root {
            --primary-color: #4a6fa5;
            --secondary-color: #166088;
            --accent-color: #4CAF50;
            --error-color: #e63946;
            --light-gray: #f8f9fa;
            --border-color: #dee2e6;
            --dark-border: #adb5bd;
            --original-color: #333;
            --solution-color: green;
        }
        
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            background-color: #f5f7fa;
            padding: 20px;
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        
        h1 {
            text-align: center;
            color: var(--secondary-color);
            margin-bottom: 1.5rem;
            font-weight: 600;
            text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
        }
        
        .container {
            display: flex;
            flex-wrap: wrap;
            gap: 2rem;
            justify-content: center;
            align-items: flex-start;
            width: 100%;
            max-width: 1200px;
        }
        
        .sudoku-container {
            background-color: white;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
            padding: 1.5rem;
            width: 100%;
            max-width: 500px;
        }
        
        .sudoku-grid {
            display: grid;
            grid-template-columns: repeat(9, minmax(30px, 1fr));
            grid-template-rows: repeat(9, minmax(30px, 1fr));
            gap: 1px;
            border: 2px solid var(--dark-border);
            margin: 0 auto;
            width: 100%;
            aspect-ratio: 1/1;
            max-width: 450px;
        }
        
        .cell {
            display: flex;
            align-items: center;
            justify-content: center;
            background-color: white;
            position: relative;
            font-size: clamp(16px, 2.5vw, 24px);
            font-weight: bold;
        }
        
        .cell input {
            width: 100%;
            height: 100%;
            border: none;
            text-align: center;
            font-size: inherit;
            font-weight: inherit;
            background-color: transparent;
            caret-color: var(--primary-color);
        }
        
        .cell input:focus {
            outline: 2px solid var(--accent-color);
            background-color: rgba(76, 175, 80, 0.05);
            z-index: 1;
        }
        
        .cell::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            border: 1px solid var(--border-color);
            pointer-events: none;
        }
        
        /* 粗边框处理 - 3x3宫格 */
        .cell:nth-child(3n):not(:nth-child(9n))::after {
            content: '';
            position: absolute;
            top: 0;
            right: -1px;
            bottom: 0;
            width: 2px;
            background-color: var(--dark-border);
        }
        
        /* 3/6行下边框 */
        .cell:nth-child(n+19):nth-child(-n+27)::before,
        .cell:nth-child(n+46):nth-child(-n+54)::before {
            border-bottom-width: 2px;
            border-bottom-color: var(--dark-border);
        }
        
        .invalid {
            color: var(--error-color) !important;
        }
        
        .original {
            color: var(--original-color);
        }
        
        .solution {
            color: var(--solution-color);
            font-weight: bold;
        }
        
        .controls-container {
            background-color: white;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
            padding: 1.5rem;
            width: 100%;
            max-width: 500px;
        }
        
        .section-title {
            color: var(--secondary-color);
            margin-bottom: 1rem;
            font-size: 1.2rem;
            font-weight: 500;
            border-bottom: 1px solid var(--border-color);
            padding-bottom: 0.5rem;
        }
        
        textarea {
            width: 100%;
            height: 60px;
            padding: 12px;
            border: 1px solid var(--border-color);
            border-radius: 6px;
            resize: none;
            font-family: 'Courier New', Courier, monospace;
            margin-bottom: 1rem;
            transition: border-color 0.3s;
        }
        
        textarea:focus {
            outline: none;
            border-color: var(--primary-color);
            box-shadow: 0 0 0 2px rgba(74, 111, 165, 0.2);
        }
        
        .button-group {
            display: flex;
            gap: 1rem;
            margin-bottom: 1.5rem;
            flex-wrap: wrap;
        }
        
        button {
            padding: 0.75rem 1.5rem;
            border: none;
            border-radius: 6px;
            font-size: 1rem;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.3s ease;
            flex-grow: 1;
            min-width: 120px;
        }
        
        #solveBtn {
            background-color: var(--accent-color);
            color: white;
        }
        
        #solveBtn:hover {
            background-color: #3d8b40;
            transform: translateY(-1px);
            box-shadow: 0 2px 8px rgba(76, 175, 80, 0.3);
        }
        
        #clearBtn {
            background-color: #f8f9fa;
            color: #495057;
            border: 1px solid var(--border-color);
        }
        
        #clearBtn:hover {
            background-color: #e9ecef;
            transform: translateY(-1px);
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
        }
        
        #exampleBtn {
            background-color: var(--primary-color);
            color: white;
        }
        
        #exampleBtn:hover {
            background-color: #3a5a8a;
            transform: translateY(-1px);
            box-shadow: 0 2px 8px rgba(74, 111, 165, 0.3);
        }
        
        .message {
            padding: 12px;
            border-radius: 6px;
            margin-bottom: 1rem;
            font-size: 0.95rem;
        }
        
        .error {
            background-color: rgba(230, 57, 70, 0.1);
            color: var(--error-color);
            border-left: 4px solid var(--error-color);
        }
        
        .result {
            background-color: rgba(248, 249, 250, 0.8);
            border: 1px solid var(--border-color);
            border-radius: 6px;
            padding: 12px;
            font-family: 'Courier New', Courier, monospace;
            white-space: pre-wrap;
            word-break: break-all;
            max-height: 200px;
            overflow-y: auto;
        }
        
        @media (max-width: 768px) {
            .container {
                flex-direction: column;
                gap: 1.5rem;
            }
            
            .sudoku-container,
            .controls-container {
                max-width: 100%;
            }
            
            .sudoku-grid {
                max-width: 100%;
                max-height: 80vw;
            }
            
            .button-group {
                flex-direction: column;
                gap: 0.75rem;
            }
            
            button {
                width: 100%;
            }
        }
    </style>
</head>
<body>
    <h1>数独解题器</h1>
    
    <div class="container">
        <div class="sudoku-container">
            <div class="sudoku-grid" id="sudokuGrid"></div>
        </div>
        
        <div class="controls-container">
            <h3 class="section-title">文本输入区</h3>
            <textarea id="textInput" placeholder="输入81位数字 (0表示空格)"></textarea>
            
            <div class="button-group">
                <button id="solveBtn">解题</button>
                <button id="clearBtn">清空</button>
                <button id="exampleBtn">示范数据</button>
            </div>
            
            <div id="errorMsg" class="message error" style="display: none;"></div>
            
            <h3 class="section-title">解题结果</h3>
            <div id="result" class="result"></div>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const grid = document.getElementById('sudokuGrid');
            const textInput = document.getElementById('textInput');
            const solveBtn = document.getElementById('solveBtn');
            const clearBtn = document.getElementById('clearBtn');
            const exampleBtn = document.getElementById('exampleBtn');
            const errorMsg = document.getElementById('errorMsg');
            const resultDiv = document.getElementById('result');
            
            // 创建数独网格
            for (let i = 0; i < 81; i++) {
                const cell = document.createElement('div');
                cell.className = 'cell';
                
                const input = document.createElement('input');
                input.type = 'text';
                input.maxLength = 1;
                input.dataset.index = i;
                input.classList.add('original');
                
                // 只允许输入1-9
                input.addEventListener('input', function(e) {
                    const value = e.target.value;
                    if (value && !/^[1-9]$/.test(value)) {
                        e.target.value = '';
                    }
                });
                
                // 离开输入框时更新文本区域
                input.addEventListener('blur', function() {
                    updateTextInput();
                    validateSudoku();
                });
                
                cell.appendChild(input);
                grid.appendChild(cell);
            }
            
            // 文本输入框变化时更新网格
            textInput.addEventListener('blur', function() {
                updateGridFromText();
                validateSudoku();
            });
            
            // 清空按钮
            clearBtn.addEventListener('click', function() {
                const inputs = document.querySelectorAll('.cell input');
                inputs.forEach(input => {
                    input.value = '';
                    input.classList.remove('invalid');
                    input.classList.remove('solution');
                    input.classList.add('original');
                });
                textInput.value = '';
                errorMsg.style.display = 'none';
                errorMsg.textContent = '';
                resultDiv.textContent = '';
            });
            
            // 示范数据按钮
            exampleBtn.addEventListener('click', function() {
                const examplePuzzle = '530070000600195000098000060800060003400803001700020006060000280000419005000080079';
                textInput.value = examplePuzzle;
                updateGridFromText();
                validateSudoku();
            });
            
            // 解题按钮
            solveBtn.addEventListener('click', function() {
                updateTextInput();
                if (validateSudoku()) {
                    solveSudoku();
                }
            });
            
            // 更新文本输入区域
            function updateTextInput() {
                const inputs = document.querySelectorAll('.cell input');
                let text = '';
                inputs.forEach(input => {
                    text += input.value || '0';
                });
                textInput.value = text;
            }
            
            // 从文本输入更新网格
            function updateGridFromText() {
                // 提取前81位数字，不足补0
                let text = textInput.value.replace(/[^0-9]/g, '').slice(0, 81);
                while (text.length < 81) {
                    text += '0';
                }
                
                const inputs = document.querySelectorAll('.cell input');
                inputs.forEach((input, index) => {
                    const value = text[index] === '0' ? '' : text[index];
                    input.value = value;
                    input.classList.remove('solution');
                    input.classList.add('original');
                });
            }
            
            // 验证数独当前状态是否符合规则
            function validateSudoku() {
                // 清除之前的错误标记
                const inputs = document.querySelectorAll('.cell input');
                inputs.forEach(input => {
                    input.classList.remove('invalid');
                });
                
                errorMsg.style.display = 'none';
                errorMsg.textContent = '';
                
                // 检查每行
                let isValid = true;
                for (let row = 0; row < 9; row++) {
                    const rowValues = [];
                    for (let col = 0; col < 9; col++) {
                        const index = row * 9 + col;
                        const value = inputs[index].value;
                        if (value && rowValues.includes(value)) {
                            // 标记重复的数字
                            for (let c = 0; c < 9; c++) {
                                const idx = row * 9 + c;
                                if (inputs[idx].value === value) {
                                    inputs[idx].classList.add('invalid');
                                }
                            }
                            isValid = false;
                        }
                        rowValues.push(value);
                    }
                }
                
                // 检查每列
                for (let col = 0; col < 9; col++) {
                    const colValues = [];
                    for (let row = 0; row < 9; row++) {
                        const index = row * 9 + col;
                        const value = inputs[index].value;
                        if (value && colValues.includes(value)) {
                            // 标记重复的数字
                            for (let r = 0; r < 9; r++) {
                                const idx = r * 9 + col;
                                if (inputs[idx].value === value) {
                                    inputs[idx].classList.add('invalid');
                                }
                            }
                            isValid = false;
                        }
                        colValues.push(value);
                    }
                }
                
                // 检查每个3x3宫
                for (let boxRow = 0; boxRow < 3; boxRow++) {
                    for (let boxCol = 0; boxCol < 3; boxCol++) {
                        const boxValues = [];
                        for (let row = 0; row < 3; row++) {
                            for (let col = 0; col < 3; col++) {
                                const actualRow = boxRow * 3 + row;
                                const actualCol = boxCol * 3 + col;
                                const index = actualRow * 9 + actualCol;
                                const value = inputs[index].value;
                                if (value && boxValues.includes(value)) {
                                    // 标记重复的数字
                                    for (let r = 0; r < 3; r++) {
                                        for (let c = 0; c < 3; c++) {
                                            const idx = (boxRow * 3 + r) * 9 + (boxCol * 3 + c);
                                            if (inputs[idx].value === value) {
                                                inputs[idx].classList.add('invalid');
                                            }
                                        }
                                    }
                                    isValid = false;
                                }
                                boxValues.push(value);
                            }
                        }
                    }
                }
                
                if (!isValid) {
                    errorMsg.textContent = '当前输入存在冲突，请检查红色标记的数字';
                    errorMsg.style.display = 'block';
                }
                
                return isValid;
            }
            
            // 解题函数
            function solveSudoku() {
                const sudokuString = textInput.value.replace(/[^0-9]/g, '').slice(0, 81).padEnd(81, '0');
                
                // 转换为二维数组
                const board = [];
                for (let i = 0; i < 9; i++) {
                    const row = [];
                    for (let j = 0; j < 9; j++) {
                        row.push(parseInt(sudokuString[i * 9 + j]));
                    }
                    board.push(row);
                }
                
                // 尝试解题
                const startTime = performance.now();
                const solved = solve(board);
                const endTime = performance.now();
                
                if (solved) {
                    // 转换回字符串
                    let solutionString = '';
                    for (let i = 0; i < 9; i++) {
                        for (let j = 0; j < 9; j++) {
                            solutionString += board[i][j];
                        }
                    }
                    
                    // 更新网格显示解
                    const inputs = document.querySelectorAll('.cell input');
                    inputs.forEach((input, index) => {
                        if (!input.value) {
                            input.value = solutionString[index];
                            input.classList.remove('original');
                            input.classList.add('solution');
                        }
                    });
                    
                    // 显示结果
                    const result = {
                        status: 'success',
                        solution: solutionString,
                        timeTaken: (endTime - startTime).toFixed(2) + 'ms'
                    };
                    resultDiv.textContent = JSON.stringify(result, null, 2);
                    errorMsg.style.display = 'none';
                } else {
                    errorMsg.textContent = '无解，请检查输入的数独题目是否正确';
                    errorMsg.style.display = 'block';
                    resultDiv.textContent = JSON.stringify({status: 'error', message: 'No solution found'}, null, 2);
                }
            }
            
            // 回溯算法解题
            function solve(board) {
                for (let i = 0; i < 9; i++) {
                    for (let j = 0; j < 9; j++) {
                        if (board[i][j] === 0) {
                            for (let num = 1; num <= 9; num++) {
                                if (isValidNum(board, i, j, num)) {
                                    board[i][j] = num;
                                    if (solve(board)) {
                                        return true;
                                    }
                                    board[i][j] = 0;
                                }
                            }
                            return false;
                        }
                    }
                }
                return true;
            }
            
            // 检查数字是否有效
            function isValidNum(board, row, col, num) {
                // 检查行
                for (let i = 0; i < 9; i++) {
                    if (board[row][i] === num) return false;
                }
                
                // 检查列
                for (let i = 0; i < 9; i++) {
                    if (board[i][col] === num) return false;
                }
                
                // 检查3x3宫
                const boxRow = Math.floor(row / 3) * 3;
                const boxCol = Math.floor(col / 3) * 3;
                for (let i = 0; i < 3; i++) {
                    for (let j = 0; j < 3; j++) {
                        if (board[boxRow + i][boxCol + j] === num) return false;
                    }
                }
                
                return true;
            }
        });
    </script>
</body>
</html>